"""
The MIT License (MIT)
Copyright © 2022 Walkline Wang (https://walkline.wang)
Gitee: https://gitee.com/walkline/espnow-remote-controller
"""
from machine import Pin, SPI, ADCBlock
from drivers.ssd1306 import SSD1306_SPI
from utils.dispatcher import Dispatcher
from config import Config


class OLED(object):
	BAUDRATE = 80_000_000

	def __init__(self):
		self.display_width = 128
		self.display_height = 32

		self.__spi = SPI(
			1,
			baudrate=self.BAUDRATE,
			sck=Pin(Config.PINS.OLED.SCLK),
			mosi=Pin(Config.PINS.OLED.SDIN)
		)

		self.painter = SSD1306_SPI(
			self.display_width, self.display_height,
			spi=self.__spi,
			dc=Pin(Config.PINS.OLED.DC),
			res=Pin(Config.PINS.OLED.RES)
		)

	def show(self):
		self.painter.show()

	def clean(self):
		self.painter.fill(0)
		self.show()


class JoyStick(object):
	def __init__(self, sampling_cb=None):
		self.__pin_list = Config.PINS.ADC.JOYSTICK_PINS
		self.__adc_list = []
		self.__last_sampling_list = []
		self.__sampling_cb = sampling_cb
		self.__adc_block = ADCBlock(1, bits=12)

		for _ in self.__pin_list:
			self.__adc_list.append(self.__adc_block.connect(_))
			self.__adc_list[-1].atten(2) # 2: ADC.ATTN_6DB: (0mV ~ 1300mV) ESP32C3
			self.__last_sampling_list.append(0)

		self.__adc_count = len(self.__adc_list)

	def deinit(self):
		for _ in self.__adc_list:
			_ = None

	def timer_callback(self, timer=None):
		sample_count = 34
		sample_avg = [0 for _ in range(self.__adc_count)]

		for _ in range(sample_count):
			for index in range(self.__adc_count):
				if _ < 2:
					self.__adc_list[index].read()
					continue

				sample_avg[index] += self.__adc_list[index].read()

		for index in range(self.__adc_count):
			sample_avg[index] >>= 5

			if abs(self.__last_sampling_list[index] - sample_avg[index]) > 7:
				self.__last_sampling_list[index] = sample_avg[index]

				if self.__sampling_cb:
					self.__sampling_cb(self.__pin_list[index], sample_avg[index])


class Coordinate(object):
	def __init__(self):
		self.__joystick_offset = [0, 0, 0, 0]
		self.__joystick_last_pos = [0, 0, 0, 0]
		self.__joystick_scale = int(4095 / 26) # 32 - 4*borders - point_width = 32 - 4*1 - 2
		self.__calibrated = 0b0

		self.__joystick = JoyStick(self.__sampling_cb)
		self.__oled = OLED()
		self.__tasks = Dispatcher(2) # 2 for ESP32C3

		self.__joystick_task = lambda: self.__joystick.timer_callback()

		self.__draw_ui_frame()

		self.__tasks.add_work(self.__joystick_task, 10)

		while not self.calibrated():
			pass

		print('offsets:', self.__joystick_offset)
		print('  scale:', self.__joystick_scale)

	def __sampling_cb(self, pin, value):
		if self.__calibrated != 0b1111:
			self.__calibrated |= 1 << pin
			self.__joystick_last_pos[pin] = value
			self.__joystick_offset[pin] = int(4095 / 2) - value

		if self.__joystick_last_pos[pin] == value:
			return

		self.__joystick_last_pos[pin] = value

		if pin % 2 == 0:
			x = int((4095 - self.__joystick_last_pos[pin] - self.__joystick_offset[pin]) / self.__joystick_scale)
			y = int((4095 - self.__joystick_last_pos[pin+1] - self.__joystick_offset[pin]) / self.__joystick_scale)
		else:
			x = int((4095 - self.__joystick_last_pos[pin-1] - self.__joystick_offset[pin]) / self.__joystick_scale)
			y = int((4095 - self.__joystick_last_pos[pin] - self.__joystick_offset[pin]) / self.__joystick_scale)

		if x > 26: x = 26
		if y > 26: y = 26

		print(f'pin{pin}: x={x}, y={y}')

		if pin in Config.PINS.ADC.JOYSTICK_1_PINS:
			self.__oled.painter.fill_rect(2, 2, 28, 28, 0)
		else:
			self.__oled.painter.fill_rect(128 - 32 + 2, 2, 28, 28, 0)

		self.__oled.painter.fill_rect(
			x + (128 - 32 if pin > 1 else 0) + 2,
			y + 2,
			2, 2,
			1
		)
		self.__oled.show()

	def __draw_ui_frame(self):
		fill_color = 1
		# self.__oled.painter.fill(1)

		self.__oled.painter.rect(0, 0, 32, 32, fill_color)
		self.__oled.painter.rect(1, 1, 30, 30, fill_color)

		self.__oled.painter.rect(128-32, 0, 32, 32, fill_color)
		self.__oled.painter.rect(128-31, 1, 30, 30, fill_color)

		# for _ in range(3, 13+1, 2):
		# 	self.__oled.painter.hline(0, _, 32, fill_color)
		# 	self.__oled.painter.vline(_, 0, 32, fill_color)

		# self.__oled.painter.rect(16-1, 16-1, 2, 2, fill_color, True)

		# for _ in range(18, 28+1, 2):
		# 	self.__oled.painter.hline(0, _, 32, fill_color)
		# 	self.__oled.painter.vline(_, 0, 32, fill_color)


		self.__oled.show()

	def calibrated(self):
		return self.__calibrated == 0b1111
	
	def j1_origin(self):
		return self.__joystick_last_pos[:2]
	
	def j2_origin(self):
		return self.__joystick_last_pos[2:]


def run_test():
	test = Coordinate()

	print(f'origins:\r\n  - j1: {test.j1_origin()}\r\n  - j2: {test.j2_origin()}')


if __name__ == '__main__':
	try:
		run_test()
	except KeyboardInterrupt:
		pass
